package qj.concurrent;

import java.util.*;

/**
 * Created by QuanLA
 * Date: Mar 30, 2006
 * Time: 5:28:55 PM
 */
public class LazyWorker implements Runnable, Workable {
    List taskList = new ArrayList();

    // Xa hoi luon ton tai nhung thang Chi Pheo
    private static final LazyWorker INSTANCE = new LazyWorker();

    private LazyWorker() {
    	this("Chi Pheo", 1, true); // Day xa hoi! he he he
    }
    
    protected LazyWorker(String name, int prio, boolean deamon) {
        Thread life = new Thread(this, name);
        if (prio>0)
        	life.setPriority(prio);
        life.setDaemon(deamon);
        life.start();
    }

    public static LazyWorker getInstance() {
        return INSTANCE;
    }

    public synchronized void schedule(Runnable task, long delay) {
    	schedule(new ServantTask(task), delay);
    }
    public synchronized void schedule(ServantTask task, long delay) {
        long absoluteTimeToProccess = System.currentTimeMillis() + delay;

        // Check if Chi Pheo got this task or not
        int size = taskList.size();
        if (size == 0) {
            // Not found
            taskList.add(new Hanger(task, absoluteTimeToProccess));
            return;
        }

        for (int i = 0; i < size; i++) {
            Hanger hanger = (Hanger) taskList.get(i);
            if (hanger.task == task) {
                // Already assigned this task,
                // Set the new time
            	if (hanger.time>absoluteTimeToProccess) {
            		// Actually postpone the task, no need to wake worker up to check.
            		this.notify();
            	}

                hanger.time = absoluteTimeToProccess;
                break;
            }

            if (i == size - 1) {
                // Not found
                taskList.add(new Hanger(task, absoluteTimeToProccess));
                this.notify();
            }
        }

        Collections.sort(taskList, HangerComparator.getInstance());
    }

    /**
     * Cancel the previously assigned task.
     * @param task
     */
    public synchronized void cancel(ServantTask task) {
        // Check if Chi Pheo got this task or not
        int size = taskList.size();

        for (int i = 0; i < size; i++) {
            Hanger hanger = (Hanger) taskList.get(i);
            if (hanger.task == task) {
                // Found the task
                // Remove it then done.
                taskList.remove(i);
                return;
            }
        }
    }

    public void run() {
        if (!Thread.currentThread().getName().equals("Chi Pheo")) {
            return;
            // Ko thang nao chiu (dc) lam thang Pheo het!
        }
        while (true) {  // Vong doi luan quan ko bao gio het
            synchronized (this) {
                long timeToDo = 0;
                long currentTime;
                
	            while (taskList.size()>0) {
                    currentTime = System.currentTimeMillis();
                    Hanger record = (Hanger) taskList.get(taskList.size() - 1);

                    timeToDo = record.time;
                    // Have to do now or not?
                    if (timeToDo <= currentTime) {
                        // Do it
                        try {
                            Servant.serveTask(record.task);
                        } finally {
                            taskList.remove(taskList.size() - 1);
                        }
                    } else
                        // Con lau moi toi gio lam viec, ngu nuong da
                        break;
                }
	            
                if (taskList.size()>0) {
                    try {
                        currentTime = System.currentTimeMillis();
                        
                        wait(timeToDo - currentTime + 5); // Ngu nuong them 5 ms nua!
                    } catch (InterruptedException e) {
                    }
                } else
                    try {
                        wait();
                    } catch (InterruptedException e) {
                    }
            }
        }
    }

    private static class Hanger {
        ServantTask task;
        long time;

        public Hanger(ServantTask task, long time) {
            this.task = task;
            this.time = time;
        }
    }
    private static final class HangerComparator implements Comparator {
        private static final HangerComparator INSTANCE = new HangerComparator();
        private HangerComparator() {}
        public static Comparator getInstance() {
            return INSTANCE;
        }

        public int compare(Object o1, Object o2) {
            try {
                return (int) (((Hanger) o2).time - ((Hanger) o1).time);
            } catch (Exception e) {
                return 0;
            }
        }
    }


}
